package org.visionlibrary.image.filters.adjustement;

import java.util.ArrayList;
import java.util.List;

import javax.media.jai.TiledImage;

import org.visionlibrary.image.acquisition.ImageFactory;
import org.visionlibrary.image.colormodel.HSVConverter;
import org.visionlibrary.image.colormodel.RGBConverter;
import org.visionlibrary.image.model.AbstractFilter;


public class HSVSegmentation extends AbstractFilter {
	private List<HSVRule> rules = new ArrayList<HSVRule>();

	@Override
	public TiledImage applyFilter(TiledImage src, TiledImage dest) {
		if (null == src)
			throw new NullPointerException("Source image is null.");

		if (null == dest)
			dest = (new ImageFactory()).createCompatibleImage(src);

		int maxX = src.getWidth();
		int maxY = src.getHeight();

		for (int x = 0; x < maxX; x++)
			for (int y = 0; y < maxY; y++) {
				float[] rgb_f = new float[src.getNumBands()];
				for (int ch = 0; ch < src.getNumBands(); ch++) {
					rgb_f[ch] = src.getSample(x, y, ch);
				}

				float[] hsv_f = (new HSVConverter())
						.convertFromTYPE_RGB(new float[] {
								(float) norm(0, 255, 0, 1, rgb_f[0]),
								(float) norm(0, 255, 0, 1, rgb_f[1]),
								(float) norm(0, 255, 0, 1, rgb_f[2]) });

				for (HSVRule r : rules) {
					boolean r1 = (Math.abs(hsv_f[0] - r.hue) <= r.htol);
					boolean r2 = (Math.abs(hsv_f[1] - r.sat) <= r.stol);
					boolean r3 = (Math.abs(hsv_f[2] - r.val) <= r.vtol);

					if (r1 && r2 && r3) {
						rgb_f = (new RGBConverter())
								.convertFromTYPE_HSV(
										new float[] { 
										(float)r.nHue,
										(float)r.nSat, 
										(float)r.nVal });

						for (int i = 0; i < rgb_f.length; i++) {
							dest.setSample(x, y, i, norm(0, 1, 0, 255, rgb_f[i]));
						}
						break;
					}
				}
			}

		return dest;
	}

	public double norm(double oMin, double oMax, double nMin, double nMax,
			double value) {
		double ratio = (double) (nMax - nMin) / (double) (oMax - oMin);
		double result = value;
		result -= oMin;
		result *= ratio;
		result += nMin;

		return result;
	}

	public void addRule(double hue, double htol, double sat, double stol,
			double val, double vtol, double nHue, double nSat, double nVal) {
		rules
				.add(new HSVRule(hue, htol, sat, stol, val, vtol, nHue, nSat,
						nVal));
	}

	public void clearRules() {
		rules.clear();
	}
}

class HSVRule {
	double hue = 0;
	double htol = 0;

	double sat = 0;
	double stol = 0;

	double val = 0;
	double vtol = 0;

	double nHue = 0;
	double nSat = 0;
	double nVal = 0;

	public HSVRule(double hue, double htollerance, double nHue, double nSat,
			double nVal) {
		this.htol = htollerance;
		this.hue = hue;
		this.nHue = nHue;
		this.nSat = nSat;
		this.nVal = nVal;
	}

	public HSVRule(double hue, double htol, double sat, double stol,
			double val, double vtol, double nhue, double nsat, double nval) {
		this.hue = hue;
		this.htol = htol;

		this.sat = sat;
		this.stol = stol;

		this.val = val;
		this.vtol = vtol;

		this.nHue = nhue;
		this.nSat = nsat;
		this.nVal = nval;
	}
}
